﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Ecopsy.DataTier;
using System.Collections;
using AgencyEntityLib.Context;

namespace AgencyEntityLib.BaseEntity
{
    /// <summary>
    /// Базовый класс дя справочников имеющих иерархическую структуру 
    /// Адресная программа,справочник типов рекламны поверхностей и т.д.
    /// </summary>
    public class TreeDictionaryItem : DictionaryItem 
    {

        public override string ID
        {
            get
            {
                return base.ID;
            }
            set
            {             
                base.ID = value;
            }
        }

        public override TreeEntitySingle ParentNode
        {
            get
            {
                return base.ParentNode;
            }
            set
            {
                //Дерево справочника - представлет коллекцию параметр - значения 
                //- где  ParamID= id узла Value= значению тек. элемента 
                ParamID = (value == null ? string.Empty : ParamID = value.ID);
                Value = ID; 
                base.ParentNode = value;
            }
        }


        public TreeDictionaryItem() : base() { }
        public TreeDictionaryItem(TreeEntitySingle Source)
            : base()
        {
            foreach (TreeEntitySingle item in Source.ChildNodes) { this.ChildNodes.Add(item); }
 
        }


        
        private string _Name = string.Empty;
        /// <summary>
        /// Соответсвует Value DictionaryItem
        /// </summary>
        public string Name { get { return _Name; } set { _Name = value; } }
        
        /// <summary>
        /// категория древовидного справочника
        /// </summary>
        private string _TypeID = string.Empty;
        public string TypeID { get { return _TypeID; } set { _TypeID = value; } }


        /// <summary>
        /// Сформирвать из дерва справочника коллекцию параметр значение, 
        /// где key- ID ветки, Value - Name ветки
        /// </summary>
        /// <param name="Dictionatry"></param>
        /// <returns></returns>
        public  Hashtable ToHashtable()
        {
            Func<TreeDictionaryItem, Hashtable, Hashtable> f = delegate(TreeDictionaryItem nd, Hashtable tbl)
            {
                tbl.Add(nd.ID, nd.Name);
                return tbl;
            };
            Hashtable r = new Hashtable();
            FindNextBranch(this, r, f);
            return r;
        }

        private void FindNextBranch(TreeDictionaryItem nd, Hashtable tbl, Func<TreeDictionaryItem, Hashtable, Hashtable> f)
        {
            f(nd, tbl);
            nd.ChildNodes.Reset();
            foreach (TreeDictionaryItem item in nd.ChildNodes)
            {
                FindNextBranch(item, tbl, f);
            }
            nd.ChildNodes.Reset();

        }


        //public HashSet<TreeDictionaryItem> ToHashSet()
        //{
        //    Func<TreeDictionaryItem, HashSet<TreeDictionaryItem>, HashSet<TreeDictionaryItem>> ff = delegate(TreeDictionaryItem nd, HashSet<TreeDictionaryItem> tbl)
        //    {
        //        tbl.Add(nd);
        //        return tbl;
        //    };
        //    HashSet<TreeDictionaryItem> t = new HashSet<TreeDictionaryItem>();
        //    FindNextBranch_(this, t, ff);
        //    return t;
        //}

        private void FindNextBranch_(TreeDictionaryItem nd, HashSet<TreeDictionaryItem> tbl, Func<TreeDictionaryItem, HashSet<TreeDictionaryItem>, HashSet<TreeDictionaryItem>> ff)
        {
            ff(nd, tbl);
            nd.ChildNodes.Reset();
            foreach (TreeDictionaryItem item in nd.ChildNodes)
            {
                FindNextBranch_(item, tbl, ff);
            }
            nd.ChildNodes.Reset();
        }

        /// <summary>
        /// УСТАРЕЛ Используется GetValueAsString
        /// Преобразует значение параметра в текстовый вид  
        /// </summary>
        /// <param name="Dictionatry"></param>
        /// <param name="Value"></param>
        /// <param name="ParamSeparator"> разделитель между параметрами </param>
        /// <param name="ItemSeparator"> разделитель между несколькими значениями одного параметра</param>
        /// <returns></returns>
        //public  string ValueAsString(   TreeDictionaryItemValue Value,
        //                                string ParamSeparator,
        //                                string ItemSeparator)
        //{
        //    if (null == this) return string.Empty;
        //    if (null == Value) return string.Empty;

        //    // сформировать табличку параметр -значение 
        //    Hashtable ParamTbl = new Hashtable();
        //    Value.ChildNodes.Reset();
        //    foreach (DictionaryItem item in Value.ChildNodes)
        //    {
        //        // если параметр со множеством значений - просто значеня через запятую

        //        if (ParamTbl[item.ParamID] != null)
        //        {
        //            List<string> ListValue = (List<string>)ParamTbl[item.ParamID];
        //            ListValue.Add(item.Value.ToString());
        //            ParamTbl[item.ParamID] = ListValue;
        //        }
        //        else
        //        {
        //            List<string> SingleListValue = new List<string>();
        //            SingleListValue.Add(item.Value.ToString());
        //            ParamTbl.Add(item.ParamID, SingleListValue);
        //        }


        //    }
        //    // Сформировать справочник в виде хэш таблички 
        //    Hashtable DictionatryTbl = this.ToHashtable();

        //    // описать функцию сопоставления текущего элемента дервва и набора параметров из ParamTbl
        //    Func<TreeDictionaryItem, Hashtable, Hashtable, StringBuilder, StringBuilder> f = delegate(TreeDictionaryItem _nd, Hashtable _ParamTbl, Hashtable _DictionatryTbl, StringBuilder Result)
        //    {

        //        // параметр не наш?
        //        if (null == ParamTbl[_nd.ID]) return Result;

        //        // ищем текстовое значение параметра                                
        //        string txt = string.Empty;
        //        List<string> sValue = (List<string>)ParamTbl[_nd.ID];
        //        // если текст значения не найден - значение не справочное а вводимое.
        //        foreach (string s in sValue)
        //        {
        //            string value = (string)_DictionatryTbl[s];
        //            if (null == value)
        //                txt = txt + (txt == string.Empty ? string.Empty : ItemSeparator) + s;
        //            else
        //                txt = txt + (txt == string.Empty ? string.Empty : ItemSeparator) + value;

        //        }

        //        Result.Append(txt);
        //        Result.Append(ParamSeparator);
        //        return Result;

        //    };

        //    StringBuilder res = new StringBuilder();
        //    FindNextDictionaryItem(this, ParamTbl, DictionatryTbl, f, res);
        //    return res.ToString();

        //}

        private  void FindNextDictionaryItem       (TreeDictionaryItem nd,
                                                    Hashtable ParamTbl,
                                                    Hashtable DictionatryTbl,
                                                    Func<TreeDictionaryItem, Hashtable, Hashtable, StringBuilder, StringBuilder> f,
                                                    StringBuilder Result)
        {
            f(nd, ParamTbl, DictionatryTbl, Result);
            nd.ChildNodes.Reset();
            if(nd.ChildNodes.Count>0)            
            foreach (TreeDictionaryItem item in nd.ChildNodes)
            {
                
                FindNextDictionaryItem(item, ParamTbl, DictionatryTbl, f, Result); ;
            }
        }

        /// <summary>
        /// Вернуть описание справочника (системный не системный - глубина) строчка из vTreeDictionaryType
        /// </summary>
        /// <param name="Dictionatry"></param>
        /// <returns></returns>
        public  TreeDictionaryTypeList_Item Get_TreeDictionaryTypeList_Item()
        {
            wsDictionary ws = (wsDictionary)AgencyLib_GlobalConstants.CurrentAgency.WS_FactoryList[WS_FactoryList.wsKey_Dictionary];
            TreeDictionaryTypeList l = ws.GetTreeDictionaryTypeList();
            return (TreeDictionaryTypeList_Item)l[this.TypeID];
        }

        public  string ValueAsString
            ( string SingleVal)
        {
            // Сформировать справочник в виде хэш таблички 
            Hashtable DictionatryTbl = ToHashtable();
            return DictionatryTbl[SingleVal].ToString();
        }



        /// <summary>
        /// строит пустое TreeDictionaryItemValue
        /// </summary>
        /// <returns></returns>

        public TreeDictionaryItemValue BuildValue()
        {
            // создаем структуру справочника 
            TreeDictionaryItemValue r = new TreeDictionaryItemValue() { Name = this.Name, TreeDictionaryTypeID = this.TypeID,ID =this.ID  };

            // Устанавливаем папку справочника
            r.TreeDictionaryFolderID = TreeDictionaryContext.GetFolderId(this.TypeID);
            return r;

        }

        /// <summary>
        /// Построить хранилище значений справочника от текущего элемента
        /// Строит цепочку параметр значение от текущего эемента к корню
        /// </summary>
        /// <param name="Dictionatry"></param>
        /// <param name="NodeId">Id ноды от которой будут строиться справочники </param>
        /// <returns></returns>
        public  TreeDictionaryItemValue BuildValue( string NodeId)
        {
            // Находим листок
            DictionaryItem Leaf = (DictionaryItem)this.Find(NodeId);
            if (null == Leaf) return null;

            // создаем структуру справочника 
            TreeDictionaryItemValue r = BuildValue();


            Func<DictionaryItem, EntityList, EntityList> fBuilder = delegate(DictionaryItem Source, EntityList NdVal)
            {
                if (Source.ParentNode != null)
                {
                    if (Source.ParentNode.ID.IndexOf(AgencyLib_GlobalConstants.qProxyId) == -1)
                    {
                        NdVal.Add(new DictionaryItem() { Value = Source.ID, ParamID = Source.ParentNode.ID });
                    }
                }

                return NdVal;
            };


            Func<string, bool> CallBack = delegate(string ID)
            {
                r.ID = ID;
                return true;
            };

            FindNext(Leaf, r.ChildNodes, fBuilder, CallBack);

            if (0 == r.ChildNodes.Count) return null;  // Значений нет ?                        

            return r;
        }



        private  void FindNext( DictionaryItem NdSource,
                                      EntityList NdVal,
                                       Func<DictionaryItem, EntityList, EntityList> fBuilder,
                                       Func<string, bool> CallBack)
        {
            fBuilder(NdSource, NdVal);
            // выскакиваем если дошли до вершины или до прокси ноды
            if (null == NdSource.ParentNode)
            {
                CallBack(NdSource.ID);
                return;
            }
            if (null != NdSource.ParentNode)
            {
                if (NdSource.ParentNode.ID.IndexOf(AgencyLib_GlobalConstants.qProxyId) > -1)
                {
                    CallBack(NdSource.ID);
                    return;
                }


                FindNext((DictionaryItem)NdSource.ParentNode, NdVal, fBuilder, CallBack);
            }

        }

        /// <summary>
        /// Сливает две ветки одного справочника в одну
        /// Подливает значения со значениями Source значения имеющие одинаковую пару тип параметра значения игнорируются
        /// ВНИМАНИЕ: Корректно работает только для выбранных значений   на одном уровне Починить!!!
        /// </summary>
        /// <param name="Dictionatry"></param>
        /// <param name="Sorce"></param>
        public  void Merge( List<DictionaryItem> Source)
        {
            DictionaryItemComparer c = new DictionaryItemComparer();
            List<DictionaryItem> result = this.ChildNodes.ToList<DictionaryItem>();
            IEnumerable r = result.Union(Source, c);
            EntityList rslt = new EntityList();
            this.ChildNodes.Reset();

            this.ChildNodes.Clear();
            foreach (DictionaryItem item in r)
            {
                this.ChildNodes.Add(item);
            }
        }


        /// <summary>
        /// Суммирует два массива  справочников в отдно дерево - на выходе справочник с общим корнем. С двумя ветками
        /// Массивы справочников - должны именть однотипные элементы
        /// </summary>
        /// <param name="BaseDictionary"></param>
        /// <param name="AddDictionary"></param>
        /// <returns></returns>
        public  TreeDictionaryItem AddArr( TreeDictionaryItem[] BaseDictionary, TreeDictionaryItem[] AddDictionary)
        {
            // хоть один системный справочник заданного типа существует
            TreeDictionaryItem first = BaseDictionary[0];
            TreeDictionaryItem res = new TreeDictionaryItem() { TypeID = first.TypeID, Name = first.Name };
            for (int i = 0; i < BaseDictionary.Length; i++)
            {
                BaseDictionary[i].ChildNodes.Reset();
                foreach (TreeDictionaryItem item in BaseDictionary[i].ChildNodes)
                {
                    res.ChildNodes.Add(item);
                }
            }

            return res;
        }

        public class DictionaryItemComparer : IEqualityComparer<DictionaryItem>
        {

            public DictionaryItemComparer()
            {

            }
            #region IEqualityComparer<DictionaryItem> Members

            public bool Equals(DictionaryItem x, DictionaryItem y)
            {
                return (x.ParamID == y.ParamID && y.Value == x.Value);
            }

            public int GetHashCode(DictionaryItem obj)
            {
                return obj.GetHashCode();
            }

            #endregion
        }

        public string  GetValueAsString(TreeDictionaryItemValue v)
        {
            TreeDictionaryItem root = (TreeDictionaryItem)this.First; 
            StringBuilder  sBuilder= new StringBuilder ();

            TreeDictionaryItemValue.Comparer cmp = new TreeDictionaryItemValue.Comparer();
            HashSet<DictionaryItem> Values = v.ChildNodes.ToHashSet<DictionaryItem>(cmp);



            
            string _RootNodeId = string.Empty;
            Func<TreeDictionaryItem, StringBuilder> fBuild = delegate(TreeDictionaryItem nd)
            {
                if (null == nd)
                    return sBuilder;
                                    
                // Корень спрваочника не выводим
                if (nd.ParentNode == null)
                {
                    _RootNodeId = nd.ID; 
                                   return sBuilder;}

                if (nd.ParentNode.ID == AgencyLib_GlobalConstants.qProxyId)
                { 
                    _RootNodeId = nd.ID; 
                                   return sBuilder;}
                ///////////////////////////////////////////////////////////////////////
                
                string term = (_RootNodeId ==nd.ParentNode .ID? "|" : " ");                                
                if (Values.Contains (nd as DictionaryItem))
                    sBuilder.Append((sBuilder.Length>0?term:" ") +nd.Name  );

                
                return sBuilder ;
            };


            NextBranch_(root,  fBuild);
            return sBuilder.ToString ();
        }

        private void NextBranch_(TreeDictionaryItem nd, Func<TreeDictionaryItem, StringBuilder> fBuild)
        {
                fBuild (nd);
                nd.ChildNodes.Reset();
                foreach (TreeDictionaryItem i in nd.ChildNodes)
                {
                    NextBranch_(i, fBuild);                    
                }
            
        }

        public static TreeDictionaryItem GetTreeDictionary(string FolderId, string DictionaryID)
        {
            wsDictionary _ws_Dictionary = new wsDictionary();
            return (TreeDictionaryItem)_ws_Dictionary.GetTreeDictionary(FolderId, DictionaryID);

        }
    }
    
}